/*
 * Copyright 2011-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.mgmt.framework.redis.autoconfigure;

import java.time.Duration;
import java.util.concurrent.ExecutionException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisException;
import io.lettuce.core.RedisURI;
import io.lettuce.core.SetArgs;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands;

/**
 * 使用 Lettuce 操作 Redis-Cluster 集群环境
 *
 * @author liym
 * @see org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
 * @since 2019-04-17 16:17 新建
 */
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
@ConditionalOnClass(RedisClient.class)
public class LettuceClusterConfiguration {

    private static final String REDIS_OK = "OK";

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final RedisProperties properties;

    public LettuceClusterConfiguration(RedisProperties properties) {
        Assert.notNull(properties, "properties must not be null");
        this.properties = properties;
    }

    /**
     * 构建 Redis-Cluster 连接
     *
     * @return Redis-Cluster 连接，使用 Spring 容器管理，可注入到其他服务中
     * @since 2019-04-17 16:30
     */
    @Bean
    @ConditionalOnMissingBean(StatefulRedisClusterConnection.class)
    public StatefulRedisClusterConnection<String, String> redisClusterConnection() {
        String host = this.properties.getHost();
        int port = this.properties.getPort();
        int database = this.properties.getDatabase();
        String password = this.properties.getPassword();
        Duration timeout = this.properties.getTimeout();

        RedisURI uri = RedisURI.create(host, port);
        uri.setDatabase(database);
        if (password != null && !password.isEmpty()) {
            uri.setPassword(password.toCharArray());
        }
        if (timeout != null) {
            uri.setTimeout(timeout);
        }
        RedisClusterClient client = RedisClusterClient.create(uri);

        // 连接 Redis-Cluster 集群环境
        StatefulRedisClusterConnection<String, String> conn;
        try {
            conn = client.connect();
        } catch (RedisException e) {
            this.logger.warn("Connect Redis-Cluster error, please check the Redis-Cluster configuration", e);
            return null;
        }

        // 测试连接是否有效
        RedisAdvancedClusterAsyncCommands<String, String> commands = conn.async();

        String result;
        try {
            SetArgs setArgs = SetArgs.Builder.ex(5L).nx();
            double randomSuffix = Math.random();
            result = commands.set("redis-client:" + randomSuffix, "lettuce:" + randomSuffix, setArgs).get();
        } catch (InterruptedException | ExecutionException e) {
            result = null;
            this.logger.error("redis set error", e);
        }

        if (REDIS_OK.equals(result)) {
            this.logger.info("Redis-Cluster connection is established for Injection!");
            return conn;
        } else {
            this.logger.warn("Redis-Cluster connection is not available, please check the Redis-Cluster configuration!");
            return null;
        }
    }

}
